﻿#include <net/tcpclient.h>
#include <net/tcpsession.h>

#include <QDateTime>
#include <QTcpSocket>
#include <QTimerEvent>

QNET_USING_NAMESPACE

TcpClient::TcpClient(ISessionListener *listener, QObject *parent) : QObject(parent)
{
    m_listener = listener;
    m_socket = new QTcpSocket(this);
    m_session = Q_NULLPTR;
    m_localAddr = QHostAddress::AnyIPv4;
    m_localPort = 0;
    m_timerID = 0;
    m_autoReconnect = true;
    m_reconnectSpan = 5000;
}

TcpClient::~TcpClient()
{
    destory();
}

void TcpClient::reconnect()
{
    if(m_session)//表示链接过一次
    {
        m_socket->abort();
        QObject::disconnect(m_session, &TcpSession::error,
                    this, &TcpClient::error);
        QObject::disconnect(m_session, &TcpSession::stateChanged,
                    this, &TcpClient::stateChanged );
        QObject::disconnect(m_session, &TcpSession::connected,
                    this, &TcpClient::connected);
        QObject::disconnect(m_session, &TcpSession::disconnected,
                    this,&TcpClient::disconnected);

        m_session->deleteLater();
        m_session = Q_NULLPTR;
    }
    m_socket->bind(m_localAddr, m_localPort);
    connect(m_remoteAddr, m_remotePort);
}

void TcpClient::connect(QHostAddress ip, quint16 port)
{
    m_remoteAddr = ip;
    m_remotePort = port;
    m_session = new TcpSession(m_socket, this);

    QObject::connect(m_session, &TcpSession::error,
                this, &TcpClient::error);
    QObject::connect(m_session, &TcpSession::stateChanged,
                this, &TcpClient::stateChanged );
    QObject::connect(m_session, &TcpSession::connected,
                this, &TcpClient::connected);
    QObject::connect(m_session, &TcpSession::disconnected,
                this,&TcpClient::disconnected);

    m_socket->connectToHost(ip, port);
}

bool TcpClient::bind(QHostAddress ip, quint16 port)
{
    m_localAddr = ip;
    m_localPort = port;
    bool bok = m_socket->bind(ip, port, QAbstractSocket::ReuseAddressHint);
    if(!bok)
    {
        QAbstractSocket::SocketError err = m_socket->error();
        qWarning() << "bind " << ip.toString() << ":" << port << "Errror:" << err;
    }
    return bok;
}

void TcpClient::disconnected()
{
    onSessionChange(false);
    startReconnectTimer();
}

void TcpClient::connected()
{    
    stopReconnectTimer();
    onSessionChange(true);
}

void TcpClient::stateChanged(QAbstractSocket::SocketState state)
{
    qDebug() << tr("TcpClient state changed:")<< state;
}
void TcpClient::error(QAbstractSocket::SocketError error)
{
    if(m_listener)
    {
        m_listener->onError(error);
    }
    // 对连接被拒绝单独处理。
    if(error == QAbstractSocket::SocketError::ConnectionRefusedError)
    {
        if(startReconnectTimer())
        {
            return;
        }
    }
    qWarning() << tr("TcpClient recv error message : ") << error << QDateTime::currentDateTime();
}

bool TcpClient::startReconnectTimer()
{
    if(m_autoReconnect && m_timerID == 0)
    {
        qWarning() << tr("TcpClient start auto reconnect")  << QDateTime::currentDateTime();
        m_timerID = startTimer(m_reconnectSpan);
        return true;
    }
    return false;
}

void TcpClient::stopReconnectTimer()
{
    if(m_timerID!=0)
    {
        killTimer(m_timerID);
        m_timerID = 0;
    }
}

void TcpClient::onSessionChange(bool connect)
{
    if(m_listener)
    {
        m_listener->onStateChanged(m_session, connect);
    }
}

quint16 TcpClient::remotePort() const
{
    return m_remotePort;
}

void TcpClient::destory()
{
    m_listener = Q_NULLPTR;
    if(m_session)
    {
        m_session->deleteLater();
        m_session = Q_NULLPTR;
    }
    if(m_socket)
    {
        m_socket->abort();
        m_socket->disconnected();
        qDebug() << "Tcp Client socket was destory！";
    }
    m_socket = Q_NULLPTR;
}

void TcpClient::setListener(ISessionListener *listener)
{
    m_listener = listener;
}

quint16 TcpClient::localPort() const
{
    return m_localPort;
}

QHostAddress TcpClient::remoteAddr() const
{
    return m_remoteAddr;
}

QHostAddress TcpClient::localAddr() const
{
    return m_localAddr;
}

bool TcpClient::autoReconnect() const
{
    return m_autoReconnect;
}

void TcpClient::setAutoReconnect(bool autoReconnect)
{
    m_autoReconnect = autoReconnect;
}

void TcpClient::timerEvent(QTimerEvent *event)
{
    if(event->timerId() == m_timerID)
    {
        stopReconnectTimer();
        reconnect();
    }
}
